package  
{
	import flash.display.Stage;
	import flash.events.KeyboardEvent;
	import flash.geom.Point;
	import flash.ui.Keyboard;
	import player.IController;
	/**
	 * ...
	 * @author Gareth Williams
	 */
	public class Gamepad implements IController
	{
		private static const CENTRE:uint 	 = 0x0000;
		
		private static const UP:uint       	 = 0x1000;
		private static const DOWN:uint     	 = 0x0100;
		private static const LEFT:uint     	 = 0x0010;
		private static const RIGHT:uint 	 = 0x0001;
		
		private static const UP_LEFT:uint    = 0x1010;
		private static const UP_RIGHT:uint   = 0x1001;
		private static const DOWN_LEFT:uint  = 0x0110;
		private static const DOWN_RIGHT:uint = 0x0101;
		
		private var recyclePoint:Point = new Point(0, 0);
		
		private var flags:uint = 0;
		private var direction:Point = new Point();
		private var stickAngle:Number = 0; 
		private var stage:Stage = null;
		
		private var fireButtonDown:Boolean = false;
		
		public var stickThrow:Number = 1;
		public var easing:Number = 0.6;
		
		public function Gamepad(stage:Stage) 
		{
			this.stage = stage;
			stage.addEventListener(KeyboardEvent.KEY_DOWN, this.onKeyInput);
			stage.addEventListener(KeyboardEvent.KEY_UP, this.onKeyRelease);
		}
		public function destructor():void
		{
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, this.onKeyInput);
			stage.removeEventListener(KeyboardEvent.KEY_UP, this.onKeyRelease);
		}
		private function onKeyInput(key:KeyboardEvent):void
		{
			//we need to process multiple keystrokes
			var keyPressed:uint = key.keyCode;
			if (keyPressed === Keyboard.UP)
				flags |= UP;
			if (keyPressed === Keyboard.DOWN)	
				flags |= DOWN;
			if (keyPressed === Keyboard.LEFT)
				flags |= LEFT;
			if (keyPressed === Keyboard.RIGHT)
				flags |= RIGHT;
			
			if (keyPressed === Keyboard.SPACE)
				this.fireButtonDown = true;
		}
		private function onKeyRelease(key:KeyboardEvent):void
		{
			//we need to process multiple keystrokes
			var keyReleased:uint = key.keyCode;
			if (keyReleased === Keyboard.UP)
				flags ^= UP;
			if (keyReleased === Keyboard.DOWN)	
				flags ^= DOWN;
			if (keyReleased === Keyboard.LEFT)
				flags ^= LEFT;
			if (keyReleased === Keyboard.RIGHT)
				flags ^= RIGHT;
				
			if (keyReleased === Keyboard.SPACE)
				this.fireButtonDown = false;
		}
		private function pollDirections():void
		{
			if (flags === CENTRE) //exit early if no pad action
			{
				recyclePoint.x = threshold(direction.x *= easing, 0.01);
				recyclePoint.y = threshold(direction.y *= easing, 0.01);
			
				this.direction = recyclePoint;
				return;
			}
			//clockwise
			//pressing the joystick in the top left corner should not produce faster diagonal movement
			//than horizontal or vertical movement hence throw * stick angle...
			//todo:maybe precalc direction values
			stickAngle = 0;
			switch(flags)
			{
				case UP:
					stickAngle = 0 * Constants.QUARTER_PI;
				break;
				case UP_RIGHT:
					stickAngle = 1 * Constants.QUARTER_PI;
				break;
				case RIGHT:
					stickAngle = 2 * Constants.QUARTER_PI;
				break;
				case DOWN_RIGHT:
					stickAngle = 3 * Constants.QUARTER_PI;
				break;
				case DOWN:
					stickAngle = 4 * Constants.QUARTER_PI;
				break;
				case DOWN_LEFT:
					stickAngle = 5 * Constants.QUARTER_PI;
				break;
				case LEFT:
					stickAngle = 6 * Constants.QUARTER_PI;
				break;
				case UP_LEFT:
					stickAngle = 7 * Constants.QUARTER_PI;
				break;
			}
			
			//todo: not sure if it's good to constantly create new points
			this.direction = Point.polar(stickThrow, stickAngle-Constants.HALF_PI);
		}
		
		private function threshold(value:Number, threshold:Number):Number
		{
			return Math.abs(value) > threshold ? value : 0;
		}
		
		/* INTERFACE player.IController */
		
		//interface
		public function getDirection():Point //read only
		{
			return this.direction;
		}
		public function getAngle():Number //read only
		{
			return this.stickAngle;
		}
		public function getFireButton():Boolean
		{
			return fireButtonDown;
		}
		public function update():void 
		{
			pollDirections();
		}
	}
}